﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ZXthex.MiniCSharp.Tests")]
namespace ZXthex
{
    public class MiniCSharpExecuter
    {
        public class ExecuteResult
        {
            public enum ResultTag
            {
                Normal,
                TypeName,
                MethodName,
                EventName,
                AutoEvent,
                DefineMacro,
                RecoverMacro,
                ListMember,
                SetValue,
                DefineVariable,
            }

            static class Helper
            {
                public static string type2string(Type t)
                {
                    if (typeKeywordDic.ContainsKey(t))
                        return typeKeywordDic[t];

                    if (t.IsGenericType)
                    {
                        var ts = t.GetGenericArguments();
                        return t.ToString().Split('`')[0] + "<" + string.Join(", ", ts.Select(tp => type2string(tp))) + ">";
                    }
                    return t.ToString();
                }
            }

            public string cmd;

            public ResultTag tag;

            public bool Error => errorIndex > -1;
            public int errorIndex = -1;
            public int errorEndIndex = -1;
            public string errorDescription;

            public Type type;
            public object obj;
            public string variableName;
            public Action<object> valueSetter;
            public bool newFlag;

            string TypeToString()
            {
                return Helper.type2string(type);
            }

            public override string ToString()
            {
                if (Error)
                {
                    int offset = cmd[errorIndex] == '.' ? 1 : 0;
                    return "analyse error in index " + errorIndex + " - " + errorEndIndex + ": "
                            + "'" + cmd.Substring(errorIndex + offset, errorEndIndex - errorIndex - offset) + "' "
                            + (errorDescription ?? "");
                }
                else if (tag == ResultTag.DefineMacro)
                {
                    return "macro defined. ";
                }
                else if (tag == ResultTag.RecoverMacro)
                {
                    return "macro undefined. ";
                }
                else if(tag == ResultTag.DefineVariable)
                {
                    return "variable defined. ";
                }
                else if (tag == ResultTag.TypeName)
                {
                    return "Type : " + TypeToString();
                }
                else if (tag == ResultTag.ListMember)
                {
                    var array = obj as MemberInfo[];
                    int count = 0;
                    string ret = "";
                    for (int i = 0; i < array.Length; i++)
                    {
                        if (count > 5 && i < array.Length - 2)
                        {
                            i = array.Length - 3;
                            ret += "...";
                            continue;
                        }
                        if (array[i] is FieldInfo fi)
                        {
                            ret += "field " + fi.Name + (fi.IsStatic ? " static" : "");
                        }
                        else if (array[i] is PropertyInfo pi)
                        {
                            var g = pi.GetGetMethod();
                            var s = pi.GetSetMethod();
                            ret += "property " + pi.Name + " " + (g == null ? "" : "g") + (s == null ? "" : "s");
                            if (g != null && g.IsStatic || s != null && s.IsStatic)
                            {
                                ret += " static";
                            }
                        }
                        else if (array[i] is MethodInfo mi)
                        {

                            string genericStr = "";
                            if (mi.IsGenericMethodDefinition)
                            {
                                genericStr = "<";
                                var gs = mi.GetGenericArguments();
                                for (int x = 0; x < gs.Length; x++)
                                {
                                    genericStr += Helper.type2string(gs[x]);
                                    if (x < gs.Length - 1)
                                        genericStr += ", ";
                                }
                                genericStr += ">";
                            }
                            string paramStr = "";
                            var ps = mi.GetParameters();
                            for (int x = 0; x < ps.Length; x++)
                            {
                                paramStr += Helper.type2string(ps[x].ParameterType) + " " + ps[x].Name;
                                if (x < ps.Length - 1)
                                    paramStr += ", ";
                            }
                            ret += "method " + mi.Name + genericStr + "(" + paramStr + ")" + (mi.IsStatic ? " static" : "");
                        }


                        if (i < array.Length - 1)
                        {
                            ret += ", ";
                        }
                        count++;
                    }
                    if (ret.Length > 0)
                        ret += ". ";
                    ret += "total count: " + array.Length;
                    return ret;
                }
                else if (tag == ResultTag.MethodName)
                {
                    return "Method from " + TypeToString() + ": " + variableName;
                }
                else if (tag == ResultTag.EventName)
                {
                    return "Event from " + TypeToString() + ": " + variableName;
                }
                else if (tag == ResultTag.AutoEvent)
                {
                    return "Auto Event from " + TypeToString() + ": " + variableName;
                }
                else if (tag == ResultTag.SetValue)
                {
                    return "value set";
                }
                else if (obj != null)
                {

                    string res = (obj.ToString());
                    string add = "";
                    if (obj is Array)
                    {
                        res += " [";
                        Array arr = obj as Array;
                        for (int z = 0; z < arr.Length; z++)
                        {
                            if (z > 5 && z < arr.Length - 2) { add = "......"; continue; }
                            if (z == arr.Length - 2) res += add;
                            res += arr.GetValue(z);
                            if (z != arr.Length - 1)
                                res += ", ";

                        }
                        res += "]   Length = " + arr.Length;
                    }

                    if (res == type.ToString())
                        res = TypeToString();

                    return res;
                }
                else if (type == typeof(void))
                {
                    return ("method executed. ");
                }
                else if (type != null)
                {
                    return "null";
                }

                return "unknown command";
            }

            public static implicit operator string(ExecuteResult result)
                => result.ToString();
        }

        static readonly char[] anySplit = { '.', '(', '<', ' ' };

        /// <summary>
        /// 核心方法。
        /// 如果输入是类名，则返回类的完全限定名
        /// 如果执行结果是数组，则返回一个含首尾元素的数组列表和数组长度
        /// 如果执行方法是void返回值，则显示“method executed”
        /// 如果完全无法解析，则显示“unknown command”
        /// </summary>
        /// <param name="cmd"></param>
        /// <returns></returns>
        public static ExecuteResult Execute(string cmd)
        {
            cmd = cmd.Trim(' ');
            ExecuteResult result = new ExecuteResult();
            result.cmd = cmd;
            if (cmd.StartsWith("#define "))
            {
                DefineMacro(cmd, result);
            }
            else if (cmd.StartsWith("dynamic "))
            {
                DefineVariable(cmd, result);
            }
            else if (cmd.Contains("=>"))
            {
                ListMember(cmd, result);
            }
            else if (cmd.Contains("="))
            {
                SetLeftRightValue(cmd, result);
            }
            else
            {
                return ExecuteInternal(cmd);
            }

            return result;
        }

        private static ExecuteResult ExecuteInternal(string cmd)
        {
            cmd = cmd.Trim(' ');
            ExecuteResult result = new ExecuteResult();
            result.cmd = cmd;
            int offsetIndex = 0;
            if (cmd.StartsWith("new "))
            {
                var subcmd = cmd.Substring(4).Trim();
                result.newFlag = true;
                offsetIndex = cmd.IndexOf(subcmd);
                cmd = subcmd;
            }

            {
                //预处理，替换起始位置的宏定义
                //为防止溢出，只做7次迭代
                {

                    for (int i = 0; i < 7; i++)
                    {
                        var firstDot = cmd.IndexOfAny(anySplit);
                        var start = firstDot > -1 ? cmd.Substring(0, firstDot).Trim() : cmd;
                        var remains = firstDot > -1 ? cmd.Substring(firstDot, cmd.Length - firstDot) : "";
                        if (macroDic.ContainsKey(start))
                        {
                            start = macroDic[start];
                        }
                        cmd = start + remains;
                    }
                }
                string read = "";
                //获取cmd的执行结果
                GetExecuteResult(cmd, ref read, result);
            }
            if (result.Error)
            {
                result.errorIndex += offsetIndex;
                result.errorEndIndex += offsetIndex;
            }
            return result;
        }

        static Dictionary<string, string> macroDic = new Dictionary<string, string>();
        private static void DefineMacro(string cmd, ExecuteResult result)
        {
            result.tag = ExecuteResult.ResultTag.DefineMacro;
            var parts = cmd.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length == 3)
            {
                var macro = parts[1];
                var content = parts[2];

                for (int i = 0; i < macro.Length; i++)
                {
                    if (!char.IsLetter(macro[i]) && macro[i] != '_')
                    {
                        result.errorIndex = 0;
                        result.errorEndIndex = cmd.Length;
                        result.errorDescription = "macro can only contain letter and '_'. ";
                        return;
                    }
                }

                if (ReadType(macro) != null)
                {
                    result.errorIndex = 0;
                    result.errorEndIndex = cmd.Length;
                    result.errorDescription = "macro can not have the same name as the type: " + macro + ". ";
                    return;
                }

                var tryVariable = ReadVariable(macro);
                if (tryVariable.type != null)
                {
                    result.errorIndex = 0;
                    result.errorEndIndex = cmd.Length;
                    result.errorDescription = "name is already used in variable names. ";
                    return;
                }

                if (macro == content)
                    result.tag = ExecuteResult.ResultTag.RecoverMacro;

                macroDic[macro] = content;
            }
            else
            {
                result.errorIndex = 0;
                result.errorEndIndex = cmd.Length;
                result.errorDescription = "macro should only has three parts seperated by space. ";
            }
        }

        static Dictionary<string, object> variableDic = new Dictionary<string, object>();
        private static void DefineVariable(string cmd, ExecuteResult result)
        {
            result.tag = ExecuteResult.ResultTag.DefineVariable;
            int dynamicLength = "dynamic".Length;
            var parts = cmd.Split(new string[] { "="}, StringSplitOptions.RemoveEmptyEntries);
            int eqIndex = cmd.IndexOf("=");
            if (eqIndex == -1)
                eqIndex = cmd.Length;

            if (parts.Length > 0 && parts.Length < 3)
            {
                var leftSideParts = parts[0].Split(new string[] { " " }, System.StringSplitOptions.RemoveEmptyEntries);
                if(leftSideParts.Length>2)
                {
                    result.errorIndex = 0;
                    result.errorEndIndex = eqIndex;
                    result.errorDescription = "variable define format error: too many parts";
                    return;
                }
                var variableName = leftSideParts[1];

                for (int i = 0; i < variableName.Length; i++)
                {
                    if (!char.IsLetter(variableName[i]) && variableName[i] != '_')
                    {
                        result.errorIndex = cmd.IndexOf(variableName);
                        result.errorEndIndex = eqIndex;
                        result.errorDescription = "variable name can only contain letter and '_'. ";
                        return;
                    }
                }

                if (ReadType(variableName) != null)
                {
                    result.errorIndex = cmd.IndexOf(variableName, dynamicLength);
                    result.errorEndIndex = eqIndex;
                    result.errorDescription = "variable name can not have the same name as the type: " + variableName + ". ";
                    return;
                }

                var tryVariable = ReadVariable(variableName);
                if(tryVariable.type != null)
                {
                    result.errorIndex = cmd.IndexOf(variableName, dynamicLength);
                    result.errorEndIndex = eqIndex;
                    result.errorDescription = "variable name already exist. ";
                    return;
                }

                object initValue = null;

                if (parts.Length == 2)
                {
                    try
                    {
                        initValue = ParseToObject(parts[1]);
                    }
                    catch (SyntaxErrorException fe)
                    {

                        var start = cmd.IndexOf(parts[1], eqIndex);
                        result.errorIndex = fe.startIndex + start;
                        result.errorEndIndex = fe.endIndex + start;
                        result.errorDescription = fe.Message;
                        return;
                    }
                    catch (System.ArgumentException)
                    {
                        result.errorIndex = eqIndex + 1;
                        result.errorEndIndex = cmd.Length;
                        result.errorDescription = "the right side should be a right value";
                        return;
                    }
                    catch
                    {
                        throw;
                    }
                }

                variableDic.Add(variableName, initValue);
            }
            else
            {
                result.errorIndex = 0;
                result.errorEndIndex = cmd.Length;
                result.errorDescription = "variable define format error";
            }
        }

        private static ExecuteResult ReadVariable(string read)
        {
            var result = new ExecuteResult();
            result.tag = ExecuteResult.ResultTag.Normal;
            if (variableDic.ContainsKey(read))
            {
                var o = variableDic[read];
                result.obj = o;
                result.type = o == null ? typeof(object) : o.GetType();
                result.valueSetter = (obj) => variableDic[read] = obj;
            }
            else
                result.type = null;
            return result;
        }

        public static ExecuteResult DefineMacro(string macro, string content)
        {
            var result = Execute("#define " + macro + " " + content);
            if (result.Error)
            {
                throw new ArgumentException(result.errorDescription);
            }
            return result;
        }

        private static void ListMember(string cmd, ExecuteResult result)
        {
            result.tag = ExecuteResult.ResultTag.ListMember;
            int middleIndex = cmd.IndexOf("=>");
            var parts = cmd.Split(new string[] { "=>" }, System.StringSplitOptions.None);
            if (parts.Length != 2)
            {
                result.errorIndex = 0;
                result.errorEndIndex = cmd.Length;
                result.errorDescription = "=> should be used like 'Type => fields|methods|fields LETTERS'";
                return;
            }
            var leftResult = ExecuteInternal(parts[0]);
            if (leftResult.tag != ExecuteResult.ResultTag.TypeName && leftResult.tag != ExecuteResult.ResultTag.Normal
                || (leftResult.tag == ExecuteResult.ResultTag.Normal
                    && !(leftResult.obj is System.Type)
                ))
            {
                result.errorIndex = 0;
                result.errorEndIndex = middleIndex;
                result.errorDescription = "the left side of the '=>' should be a type or an instance of System.Type class";
                return;
            }
            else if (leftResult.Error)
            {
                result.errorIndex = leftResult.errorIndex;
                result.errorEndIndex = leftResult.errorEndIndex;
                result.errorDescription = leftResult.errorDescription;
                return;
            }
            var type = leftResult.type;
            if (leftResult.tag == ExecuteResult.ResultTag.Normal)
                type = leftResult.obj as Type;

            var rightParts = parts[1].Split(new string[] { " " }, System.StringSplitOptions.RemoveEmptyEntries);
            if (rightParts.Length < 1)
            {
                result.errorIndex = middleIndex;
                result.errorEndIndex = cmd.Length;
                result.errorDescription = "the right side of the '=>' should not be empty ";
                return;
            }

            result.type = typeof(string[]);
            List<MemberInfo> memberList = new List<MemberInfo>(100);
            if (rightParts[0] == "fields" || rightParts[0].ToLower() == "f")
            {
                var fs = type.GetFields();
                memberList.AddRange(fs);
            }
            else if (rightParts[0] == "properties" || rightParts[0].ToLower() == "p")
            {
                var ps = type.GetProperties();
                memberList.AddRange(ps);
            }
            else if (rightParts[0] == "methods" || rightParts[0].ToLower() == "m")
            {
                var ms = type.GetMethods();
                memberList.AddRange(ms);
            }
            else if (rightParts[0] == "any" || rightParts[0].ToLower() == "a")
            {
                var ms = type.GetMembers();
                memberList.AddRange(ms);
            }
            else
            {
                result.errorIndex = cmd.IndexOf(rightParts[0], middleIndex);
                result.errorEndIndex = result.errorIndex + rightParts[0].Length;
                result.errorDescription = "shoud use one of following words: fields, properties, methods, any, f, p, m, a";
                return;
            }

            if (rightParts.Length > 1)
            {
                for (int i = 0; i < memberList.Count; i++)
                {
                    bool continueFlag = false;
                    for (int j = 1; j < rightParts.Length; j++)
                    {
                        if (memberList[i].Name.Contains(rightParts[j]))
                        {
                            continueFlag = true;
                            break;
                        }
                    }
                    if (continueFlag)
                        continue;

                    memberList.RemoveAt(i);
                    i--;
                }
            }
            result.obj = memberList.ToArray();
        }

        private static object ParseToObject(string str)
        {
            str = str.Trim();
            try
            {
                return ParseLiteral(str);
            }
            catch
            {
                var result = ExecuteInternal(str);
                if (result.Error)
                {
                    throw new SyntaxErrorException(result.errorDescription, result.errorIndex, result.errorEndIndex);
                }
                else if (result.tag != ExecuteResult.ResultTag.Normal)
                {
                    throw new System.ArgumentException("This is not a right value");
                }
                return result.obj;
            }
        }

        private static object ParseLiteral(string str)
        {
            str = str.Trim();
            return str == "true" ? (object)true
                        : str == "false" ? (object)false
                        : str == "null" ? null
                        : str[0] == '\"' ? str.Replace("\"", "")
                        : str[0] == '\'' ? str.Replace("\'", "")[0]
                        : str.Contains(".") || str.Contains("f") ? (object)float.Parse(str.Substring(0, str.Length - 1))
                        : (object)int.Parse(str);
        }

        private static void SetLeftRightValue(string cmd, ExecuteResult result)
        {
            result.tag = ExecuteResult.ResultTag.SetValue;
            var middleIndex = cmd.IndexOf("=");
            var parts = cmd.Split(new string[] { "=" }, System.StringSplitOptions.None);
            if (parts.Length != 2)
            {
                result.errorIndex = 0;
                result.errorEndIndex = cmd.Length;
                result.errorDescription = "= should be used like 'Left value = Right value";
                return;
            }
            var leftValue = ExecuteInternal(parts[0]);
            if (leftValue.Error)
            {
                result.errorIndex = leftValue.errorIndex;
                result.errorEndIndex = leftValue.errorEndIndex;
                result.errorDescription = leftValue.errorDescription;
                return;
            }
            else if (leftValue.tag != ExecuteResult.ResultTag.Normal
                || leftValue.tag == ExecuteResult.ResultTag.Normal
                    && leftValue.valueSetter == null)
            {
                result.errorIndex = 0;
                result.errorEndIndex = middleIndex;
                result.errorDescription = "the left side should be a left value";
                return;
            }
            object rightValue;
            try
            {
                rightValue = ParseToObject(parts[1]);
            }
            catch (SyntaxErrorException fe)
            {

                var start = cmd.IndexOf(parts[1], middleIndex);
                result.errorIndex = fe.startIndex + start;
                result.errorEndIndex = fe.endIndex + start;
                result.errorDescription = fe.Message;
                return;
            }
            catch (System.ArgumentException)
            {
                result.errorIndex = middleIndex + 1;
                result.errorEndIndex = cmd.Length;
                result.errorDescription = "the right side should be a right value";
                return;
            }
            catch
            {
                throw;
            }

            try
            {
                leftValue.valueSetter(rightValue);
            }
            catch (System.Exception e)
            {
                result.errorIndex = 0;
                result.errorEndIndex = cmd.Length;
                result.errorDescription = e.Message;
            }
        }

        private static void GetExecuteResult(string cmd, ref string read, ExecuteResult result)
        {
            BindingFlags bf = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
            int i = 0;
            int lastI = i;
            try
            {
                CodeBlockReader reader = new CodeBlockReader(cmd, '.', result.newFlag);

                while (reader.Read(ref i, ref read))
                {
                    Analyse(ref read, result, bf);
                    lastI = i;
                }
                Analyse(ref read, result, bf);

                if (result.type == null)
                {
                    result.errorDescription = "type name not found";
                    throw new Exception();
                }
                if (result.newFlag)
                {
                    result.errorDescription = "no custructor found";
                    throw new Exception();
                }
            }
            catch
            {
                result.errorIndex = lastI;
                result.errorEndIndex = i;
            }
        }

        private static void Analyse(ref string read, ExecuteResult result, BindingFlags bf)
        {
            const string MEMBER_NAME_NOT_FOUND = "member name not found";
            if (read.Length > 0 && read[0] == '.')
                read = read.Substring(1);

            if (result.type == null)
            {
                result.type = ReadType(read);

                if (result.type != null)
                {
                    result.tag = ExecuteResult.ResultTag.TypeName;
                    read = "";
                }
                else if(!read.Contains("."))
                {
                    var res = ReadVariable(read);
                    if(res.type!= null)
                    {
                        result.type = res.type;
                        result.obj = res.obj;
                        result.valueSetter = res.valueSetter;

                        read = "";
                    }
                }
            }
            else
            {
                if (result.type != null && result.obj == null && result.tag == ExecuteResult.ResultTag.Normal)
                {
                    result.errorDescription = "null reference can't access any member";
                    throw new Exception();
                }

                result.tag = ExecuteResult.ResultTag.Normal;

                if (read.Contains("(") && read.Contains(")"))
                {
                    var memberStr = read.Split('(', ')')[0];
                    var paramStr = read.Substring(memberStr.Length).Trim(' ');
                    memberStr = memberStr.Trim(' ');
                    var start = paramStr.IndexOf('(');
                    var end = paramStr.LastIndexOf(')');
                    if (start == end - 1)
                        paramStr = "";
                    else
                        paramStr = paramStr.Substring(start + 1, end - start - 1);

                    Type[] Ts = null;
                    if (memberStr.Contains("<") && memberStr.Contains(">"))
                    {
                        var typeStr = memberStr.Split('<', '>')[1].Replace(" ", "").Split(',');
                        Ts = new Type[typeStr.Length];
                        for (int i = 0; i < typeStr.Length; i++)
                        {
                            Ts[i] = ReadType(typeStr[i]);
                        }
                        memberStr = memberStr.Split('<')[0];
                        memberStr = memberStr.Trim(' ');
                    }

                    var paramStrList = new List<string>();
                    {
                        var reader = new CodeBlockReader(paramStr, ',');
                        string p = "";
                        int z = 0;
                        while (reader.Read(ref z, ref p))
                        {
                            p = p.Trim(' ');
                            if (p[0] == ',') p = p.Substring(1);
                            p = p.Trim(' ');
                            paramStrList.Add(p);
                            p = "";
                        }
                        p = p.Trim(' ');
                        if (p.Length > 0 && p[0] == ',') p = p.Substring(1);
                        p = p.Trim(' ');
                        paramStrList.Add(p);
                    }
                    var paramSec = paramStrList.ToArray();
                    var param = new object[paramSec.Length];
                    if (paramSec.Length == 1 && paramSec[0] == "")
                        param = null;
                    else
                        for (int z = 0; z < param.Length; z++)
                        {
                            paramSec[z] = paramSec[z].Trim(' ');
                            try
                            {
                                param[z] = ParseToObject(paramSec[z]);
                                //.Replace("\u0000","\\\"")
                            }
                            catch
                            {
                                string r = "";
                                ExecuteResult res = new ExecuteResult();
                                GetExecuteResult(paramSec[z], ref r, res);
                                if (res.Error)
                                {
                                    result.errorDescription = res.errorDescription;
                                    throw new Exception();
                                }
                                param[z] = res.obj;
                            }

                        }

                    var tt = result.type;
                    Type bt = null;
                    bool flag;

                    void HandleInvokeResult(MethodInfo method, object returnValue, ExecuteResult er)
                    {
                        var returnType = method.ReturnType;
                        if (returnType != typeof(void))
                        {
                            result.type = returnValue == null ? returnType : returnValue.GetType();
                            result.obj = returnValue;
                        }
                        else
                        {
                            result.type = typeof(void);
                            result.obj = null;
                        }
                    }

                    do
                    {
                        bt = tt.BaseType;
                        MemberInfo[] ms;
                        if (result.newFlag)
                        {
                            ms = tt.GetConstructors();
                        }
                        else
                        {
                            ms = tt.GetMember(memberStr, bf);
                        }

                        flag = false;
                        for (int z = 0; z < ms.Length; z++)
                        {
                            try
                            {
                                if (ms[z] is MethodInfo mi)
                                {
                                    if (mi.IsGenericMethod) mi = mi.MakeGenericMethod(Ts);

                                    var o = mi.Invoke(result.obj, param);
                                    HandleInvokeResult(mi, o, result);

                                    flag = true;
                                    break;
                                }
                                else if (ms[z] is ConstructorInfo ci)
                                {
                                    var o = ci.Invoke(param);

                                    result.type = o.GetType();
                                    result.obj = o;
                                    result.newFlag = false;
                                    flag = true;
                                    break;
                                }
                                else
                                {
                                    object d = null;
                                    if (ms[z] is FieldInfo fi)
                                        d = fi.GetValue(result.obj);
                                    else if (ms[z] is PropertyInfo pi)
                                        d = pi.GetValue(result.obj);

                                    if (d is Delegate dele)
                                    {
                                        var dmi = dele.GetMethodInfo();
                                        var o = dele.DynamicInvoke(param);
                                        HandleInvokeResult(dmi, o, result);

                                        flag = true;
                                        break;
                                    }

                                }
                            }
                            catch (Exception)
                            {
                                continue;
                            }
                        }

                        if (flag) break;
                        if (result.newFlag) break;
                        tt = bt;
                    } while (bt != typeof(object));

                    {
                        if (!flag && memberStr == "Invoke"
                          && result.obj is Delegate dele)
                        {
                            var dmi = dele.GetMethodInfo();
                            var o = dele.DynamicInvoke(param);
                            HandleInvokeResult(dmi, o, result);

                            flag = true;
                        }
                    }

                    {
                        if (!flag && result.newFlag && result.type.IsValueType && param == null)
                        {
                            result.obj = Activator.CreateInstance(result.type);
                            result.newFlag = false;
                            flag = true;
                        }
                    }

                    if (!flag)
                    {
                        result.errorDescription = MEMBER_NAME_NOT_FOUND;
                        throw new Exception();
                    }
                }
                else
                {
                    var ms = result.type.GetMember(read, bf);
                    if (ms.Length > 0)
                    {
                        var m = ms[0];
                        if (m is FieldInfo f)
                        {
                            var invokeInstance = result.obj;
                            result.valueSetter = (o) =>
                            {
                                f.SetValue(invokeInstance, o);
                            };
                            result.obj = f.GetValue(result.obj);
                            result.type = result.obj == null ? f.FieldType : result.obj.GetType();
                        }
                        else if (m is PropertyInfo p)
                        {
                            var invokeInstance = result.obj;
                            var sm = p.GetSetMethod();
                            if (sm != null)
                            {
                                result.valueSetter = (o) =>
                                {
                                    p.SetValue(invokeInstance, o);
                                };
                            }
                            result.obj = p.GetValue(result.obj, null);
                            result.type = result.obj == null ? p.PropertyType : result.obj.GetType();
                        }
                        else if (m is MethodInfo mt)
                        {
                            result.tag = ExecuteResult.ResultTag.MethodName;
                            result.variableName = mt.Name;
                        }
                        else if (m is EventInfo e)
                        {
                            if (ms.Length > 1 && ms[1] is FieldInfo ef)
                            {
                                result.tag = ExecuteResult.ResultTag.AutoEvent;
                                result.obj = ef.GetValue(result.obj);
                                result.type = result.obj == null ? ef.FieldType : result.obj.GetType();
                                result.variableName = e.Name;
                            }
                            else
                            {
                                result.tag = ExecuteResult.ResultTag.EventName;
                                result.variableName = e.Name;
                            }
                        }
                        else if (m is Type t)
                        {
                            result.tag = ExecuteResult.ResultTag.TypeName;
                            result.type = t;
                            result.obj = null;
                        }
                    }
                    else
                    {
                        result.errorDescription = MEMBER_NAME_NOT_FOUND;
                        throw new Exception();
                    }

                }

                read = "";
            }
        }

        static readonly Dictionary<Type, string> typeKeywordDic = new Dictionary<Type, string>
        {
            [typeof(bool)] = "bool",
            [typeof(byte)] = "byte",
            [typeof(char)] = "char",
            [typeof(decimal)] = "decimal ",
            [typeof(double)] = "double ",
            [typeof(float)] = "float",
            [typeof(int)] = "int",
            [typeof(long)] = "long",
            [typeof(object)] = "object",
            [typeof(sbyte)] = "sbyte",
            [typeof(short)] = "short",
            [typeof(string)] = "string",
            [typeof(uint)] = "uint",
            [typeof(ulong)] = "ulong",
            [typeof(ushort)] = "ushort",

        };
        static readonly Dictionary<string, Type> keywordTypeDic = new Dictionary<string, Type>
        {
            ["bool"] = typeof(bool),
            ["byte"] = typeof(byte),
            ["char"] = typeof(char),
            ["decimal "] = typeof(decimal),
            ["double "] = typeof(double),
            ["float"] = typeof(float),
            ["int"] = typeof(int),
            ["long"] = typeof(long),
            ["object"] = typeof(object),
            ["sbyte"] = typeof(sbyte),
            ["short"] = typeof(short),
            ["string"] = typeof(string),
            ["uint"] = typeof(uint),
            ["ulong"] = typeof(ulong),
            ["ushort"] = typeof(ushort),
        };

        internal static Type ReadType(string read)
        {
            if (keywordTypeDic.ContainsKey(read))
                return keywordTypeDic[read];

            string[] namespaces = { "UnityEngine", "UnityEngine.UI" };
            read = read.Trim(' ');
            Type type = Type.GetType(read);
            if (type != null)
                return type;

            var asms = AppDomain.CurrentDomain.GetAssemblies();
            for (int i = 0; i < asms.Length; i++)
            {
                type = asms[i].GetType(read);
                if (type != null)
                    return type;

                for (int j = 0; j < namespaces.Length; j++)
                {
                    type = asms[i].GetType(namespaces[j] + "." + read);
                    if (type != null)
                        return type;
                }


            }

            return null;
        }

    }

    class CodeBlockReader
    {
        string content;
        char stopCode;
        bool stopBeforeFirstParenthesis;
        bool firstParenthesisMatch;

        public CodeBlockReader(string content, char stopCode)
            : this(content, stopCode, false)
        {
        }

        public CodeBlockReader(string content, char stopCode, bool stopBeforeFirstParenthesis)
        {
            this.content = content;
            this.stopCode = stopCode;
            this.stopBeforeFirstParenthesis = stopBeforeFirstParenthesis;
        }

        public bool Read(ref int i, ref string codeBlock)
        {
            bool first = true;
            bool inSQ = false, inDQ = false;
            int inParenthesisCount = 0;
            for (; i < content.Length; i++)
            {
                if (inSQ)
                {
                    if (content[i] == '\'')
                    {
                        inSQ = false;
                    }
                }
                else if (inDQ)
                {
                    if (content[i] == '\"')
                        inDQ = false;
                }
                else if (inParenthesisCount > 0)
                {
                    if (content[i] == ')')
                        inParenthesisCount--;
                    else if (content[i] == '(')
                        inParenthesisCount++;
                }
                else if (content[i] == '(')
                {
                    inParenthesisCount = 1;
                    if (!firstParenthesisMatch)
                    {
                        firstParenthesisMatch = true;
                        if (stopBeforeFirstParenthesis)
                        {
                            return true;
                        }
                    }
                }
                else if (content[i] == '\'')
                {
                    inSQ = true;
                }
                else if (content[i] == '\"')
                {
                    inDQ = true;
                }
                else if (content[i] == stopCode && first)
                {

                }
                else if (content[i] == stopCode)
                {

                    return true;
                }
                codeBlock += content[i];
                first = false;
            }
            return false;
        }
    }

    class SyntaxErrorException : Exception
    {
        public int startIndex { get; private set; }
        public int endIndex { get; private set; }
        public SyntaxErrorException(string message, int startIndex, int endIndex)
            : base(message)
        {
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }
    }
}